#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import platform
import time

from gaussdbt.resource.gaussdbt_resource import GaussCluster
from gaussdbt.commons.const import Env, NormalErr, ErrorCode, ClusterStatus, RETRY_WAIT_SECONDS, RETRY_TIMES, \
    IS_CLONE_FILE_SYSTEM
from gaussdbt.commons.database_common import gaussdbt_check_user_name_and_injection
from common.common import check_dir_uid_gid, check_dir_authority
from common.const import RoleType, JobData
from common.logger import Logger
from common.parse_parafile import get_user_name

if platform.system().lower() == "linux":
    import pwd

log = Logger().get_logger("gaussdbt_plugin.log")


class CheckRestore:
    def __init__(self, inputs: {}):
        self.input_info = inputs
        self.new_media_path = self.input_info.get("new_media_path")
        self.new_meta_path = self.input_info.get("new_meta_path")
        self.check_data = self.input_info.get("check_data")
        self.check_meta = self.input_info.get("check_meta")

    @staticmethod
    def check_cluster_status():
        for _ in range(RETRY_TIMES):
            cluster_status = GaussCluster.get_cluster_state()
            if cluster_status in ClusterStatus:
                return NormalErr.NO_ERR
            else:
                log.info(f"Failed getting status, wait for {RETRY_WAIT_SECONDS} seconds")
                time.sleep(RETRY_WAIT_SECONDS)
        return ErrorCode.ERR_DATABASE_STATUS

    def check_version(self):
        present_version = GaussCluster.get_gdb_version()
        if not self.input_info.get("version"):
            log.error(f'No version info!')
            return NormalErr.FALSE
        if present_version.startswith(self.input_info.get("version")):
            return NormalErr.NO_ERR
        else:
            log.error(f'Version not matched! {present_version} not equal to {self.input_info.get("version")}')
            return ErrorCode.ERROR_DIFFERENT_VERSION

    def check_node_and_topo(self):
        nodes = GaussCluster.get_all_node()
        count_primary = 0
        for node in nodes:
            if node.node_role == str(RoleType.PRIMARY.value):
                count_primary += 1
        if len(nodes) != self.input_info.get("node_count") or count_primary != self.input_info.get("primary_count"):
            log.error(f'Nodes not matched! local primary:{count_primary} is not equal to remote primary:'
                      f'{self.input_info.get("primary_count")}')
            return ErrorCode.ERROR_DIFFERENT_TOPO
        return NormalErr.NO_ERR

    def check_mount(self):
        # 检查目录权限
        check_rights = NormalErr.NO_ERR
        database_user = get_user_name(f"{Env.USER_NAME}_{JobData.PID}")
        if gaussdbt_check_user_name_and_injection(database_user):
            return NormalErr.FALSE
        for check_path in [self.check_meta, self.check_data]:
            if database_user != pwd.getpwuid(os.stat(check_path).st_uid).pw_name:
                log.error(f'Path {check_path} owner is not database user')
                check_rights = ErrorCode.ERROR_MOUNT_PATH
                break
            if self.input_info.get(IS_CLONE_FILE_SYSTEM, True) and not check_dir_authority(check_path, 700):
                log.error(f'Path {check_path} authority is not equal to required')
                check_rights = ErrorCode.ERROR_MOUNT_PATH
                break
        return check_rights

    def check_uid_gid(self):
        """
        前置任务
        :return:
        """
        user_name = get_user_name(f"{Env.USER_NAME}_{JobData.PID}")
        if gaussdbt_check_user_name_and_injection(user_name):
            log.error("Failed to get user name")
            return NormalErr.FALSE
        try:
            # 获取数据库用户ID和组ID
            user_id, group_id = GaussCluster.get_user_info(user_name)
        except Exception:
            log.exception("Exception when exec pre task")
            return NormalErr.FALSE
        if not check_dir_uid_gid(self.new_media_path, user_id, group_id):
            log.error(f'Path: {self.new_media_path} is not available because of different uid or gid')
            return ErrorCode.ERROR_DIFFERENT_USER
        if not check_dir_uid_gid(self.new_meta_path, user_id, group_id):
            log.error(f'Path: {self.new_meta_path} is not available because of different uid or gid')
            return ErrorCode.ERROR_DIFFERENT_USER
        return NormalErr.NO_ERR

    def pre_restore_job(self):
        """
        前置任务
        :return:
        """
        log.info("Start to exec pre task")
        check_result = self.check_cluster_status()
        if check_result != NormalErr.NO_ERR:
            return check_result
        check_result = self.check_version()
        if check_result != NormalErr.NO_ERR:
            return check_result
        check_result = self.check_node_and_topo()
        if check_result != NormalErr.NO_ERR:
            return check_result
        check_result = self.check_uid_gid()
        if check_result != NormalErr.NO_ERR:
            return check_result
        check_result = self.check_mount()
        if check_result != NormalErr.NO_ERR:
            return check_result
        log.info("Execute pre task succeed")
        return NormalErr.NO_ERR
